#include <stdio.h>
#include <stdlib.h>
#include <cuda.h>
#include <curand_kernel.h>
#include <iostream>
#include <thrust/device_vector.h>
#include <time.h>
#include <omp.h>
#include <vector>
#include <sys/time.h> 
#include <thrust/sequence.h>
#include <thrust/partition.h>
using namespace std;



struct KernelInit{
	curandState* states;
	
	KernelInit(curandState* _states){ states = _states;}
	
	__device__
	void operator()(unsigned int& seed){
		int idx = threadIdx.x + blockIdx.x * blockDim.x;
		
		unsigned long long seq1 = 1000000;
		unsigned long long seq2 = 1000000;
		
		curand_init(seed,seq1*seq2*seq1,seq1,&states[idx]);
	}
};

__device__ void rand_test(curandState& rand_obj){
	double debug_num = curand_uniform(&rand_obj);
	printf("debug num is %f and threadIdx id %d \n",debug_num, (blockDim.x * blockIdx.x + threadIdx.x));
}

__global__ void test(curandState* rand_vec){
	int idx = blockDim.x * blockIdx.x + threadIdx.x;
	curandState local_rand = rand_vec[idx];
	
	if(idx == 5){
	for(int i = 0; i < 10; i++){
		rand_test(local_rand);
	}
	}
}

struct Person{
	int id[128];
};

__global__ void personCompute(Person* pvec, int numThreads, int total){
	int idx = threadIdx.x + blockIdx.x * blockDim.x;
	for(int i = 0; i < 1500; i++){
		double k = sin((double)idx);
		k += sin(k);
		k -= cos(k);
	}
	int size = total / numThreads + (total % numThreads != 0);
	int start_idx = size * idx;
	int end_idx= start_idx + size;
	end_idx = end_idx < total ? end_idx : total;

	for(int i = start_idx; i < end_idx; i++){
		for(int j = 0; j < 128; j++){
			pvec[i].id[j] = idx;
		}
	}
}

struct RandGen{

	__device__
	double operator()(curandState& states){
		return curand_uniform(&states);
	}
};

void testOpenMp(double& k){
	#pragma omp parallel for
	for(int j = 0; j < 3; j++){
		for(double m = 0; m < 10000; m += 0.003) k += sin(k);
		k -= cos(k); 
	}
}

struct testPart{
	bool* v;
	testPart(bool* _v){v = _v;}
	__host__ __device__
	bool operator()(const int& pid){
		return v[pid];
	}
};

__global__ void kernelTest(int* idx){
	int id = blockIdx.x*blockDim.x + threadIdx.x;
	idx[id] = 1;
}

int main(){

	struct timeval start, finish;
	double t1,t2;

	srand(time(NULL));
	
	int array_size = 1000000;
	int threads_per_block = 1000;
	int num_blocks = 100;
	int num_true = 10000;

	thrust::device_vector<bool> v(array_size,false);
	vector<bool> hv(array_size,false);

	for(int i = 0; i < num_true; i++){
		v[rand()%array_size] = true;
		hv[rand()%array_size] = true;
	}

	thrust::device_vector<int> idx(array_size);
	thrust::sequence(idx.begin(),idx.end());
	vector<int> hidx(array_size);
	thrust::sequence(hidx.begin(),hidx.end());


	gettimeofday(&start,NULL);

	kernelTest<<<5000,128>>>(thrust::raw_pointer_cast(idx.data()));
	cudaError err = cudaThreadSynchronize();
	 if(err != cudaSuccess)
	 {
		 // print the CUDA error message and exit
		 printf("CUDA error: %s\n", cudaGetErrorString(err));
		 getchar();
	 }

	gettimeofday(&finish,NULL);
	t1 = (double)start.tv_sec + ((double)start.tv_usec/1000000.0);
	t2 = (double)finish.tv_sec + ((double)finish.tv_usec/1000000.0);
	cout << "gpu taken is: " << (t2 - t1) << " secs" << endl;	


	return 0;
}
